﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Mvc;
using ImpromptuInterface;
using ImpromptuInterface.Dynamic;
using System.Linq;
using System.Web.Routing;
using MVCUIPlugins.Templates;

namespace MVCUI
{
    public static class DataGrid
    {
        private static string gridName= "";
        public static MvcHtmlString DataGridView(this HtmlHelper helper, IQueryable<dynamic> Model, string GridName, GridConfiguration configuration, DataGridColumns columns, Func<dynamic, dynamic> deleteTemplate = null, Func<dynamic, dynamic, dynamic> editTemplate = null)
        {
            IEnumerable<dynamic> model=Model;
            int itemsNumber = Model.Count();
            int skip = 1;
            gridName = GridName;
           
            var context = HttpContext.Current.Request.RequestContext;
            string absUri = context.HttpContext.Request.Url.AbsoluteUri;

            if (GetQueryParam(context, GridName, "sort") != null)
            {
                if (itemsNumber == 0)
                    return new MvcHtmlString("");

                string sortColumn = GetQueryParam(context, GridName, "sort");
                var item = model.First();
  
                IEnumerable<string> membersName= Impromptu.GetMemberNames(item);
                var isMember = membersName.AsQueryable().Contains(sortColumn);
                  
                if (isMember)
                {
                    if (GetQueryParam(context, GridName, "sorttype") == "desc")
                        model = Model.AsEnumerable().OrderByDescending(x => Impromptu.InvokeGet(x, sortColumn));
                    if (GetQueryParam(context, GridName, "sorttype") == "asc")
                        model = Model.AsEnumerable().OrderBy(x => Impromptu.InvokeGet(x, sortColumn));
                }
                
            }

            string pageParam = GetQueryParam(context, GridName, "page");
            if (pageParam != null)
            {
                try
                {
                    skip = Int32.Parse(pageParam);
                }
                catch(Exception e){}

                model = model.Skip((skip-1) * (configuration.PageSize)).Take(configuration.PageSize);
            }
            else
            {
                // Create tag builder
                model = model.Take(configuration.PageSize);
            }

   

            StringBuilder sb = new StringBuilder();
            

            if (IsEditMode(GridName, context))
            {
                sb.AppendLine(String.Format("<form  id='{0}UpdateForm' name='{0}UpdateForm' action='{1}' method='POST'>", GridName, String.Format("{0}{1}", configuration.UpdateAction, GetParamIfNotEmpty(GridName, "page", pageParam))));
            }

            sb.AppendLine(String.Format("<table class='table table-hover table-condensed' id='{0}'", GridName));

                     
            //inpromptu
            sb.Append("<thead><tr>");
            columns.Columns.Add(new TextColumn("Id", "Id"));

            //create table header
            foreach (Column column in columns.Columns)
            {
                if (column.DataField == "Id")
                    sb.Append("<th style='display:none;'>");
                else
                    sb.Append("<th>");

                if (configuration.Sortable && (column as PredefinedColumn)==null)
                {

                    
                    if (GetQueryParam(context, GridName, "sorttype") == null || GetQueryParam(context, GridName, "sorttype") == "asc")
                    {
                        string editUrl = absUri.SetUrlParameter("sort", column.DataField, GridName).SetUrlParameter("sorttype", "desc", GridName).RemoveUrlParameter("page", GridName);
                        sb.AppendLine(String.Format("<a href='{0}'>{1}</a>", editUrl, column.HeaderName, "desc"));
                    }
                    else
                    {
                        string editUrl = absUri.SetUrlParameter("sort", column.DataField, GridName).SetUrlParameter("sorttype", "asc", GridName).RemoveUrlParameter("page", GridName);
                        sb.AppendLine(String.Format("<a href='{0}'>{1}</a>", editUrl, column.HeaderName, "asc"));

                    }
                    
                }
                else
                {
                    sb.Append(column.HeaderName);
                }
                sb.Append("</th>");
            }



            sb.Append("</tr></thead>");
            sb.Append("<tbody>");

            //add table data
            foreach (dynamic modelItem in model)
            {

               sb.Append("<tr id='"+ modelItem.Id +"' bgColor"+modelItem.Id+">");
                    
                string objectName = modelItem.GetType().Name;
                foreach (Column column in columns.Columns)
                {
                    if (column.DataField == "Id")
                        sb.Append("<td style='display:none;'>");
                    else
                        sb.Append("<td>");


                    
                    

                    dynamic value = "";
                    PredefinedColumn prefCol= (column as PredefinedColumn);
                    if (prefCol!=null)
                    {
                        string id = System.Convert.ToString(modelItem.Id);
                        if (prefCol.Column == PreDefinedColumnsTypes.Delete)
                        {

                            string delId = GetQueryParam(context, GridName, "deleteid");
                            if (String.IsNullOrEmpty(delId))
                            {
                                string editUrlParam = absUri.SetUrlParameter("deleteid", id, GridName).RemoveUrlParameter("edit", GridName);
                                value= column.GetHtml(modelItem);
                                if(value==null)
                                    value = value + String.Format("<a href='{2}' class='{1}Delete' id='{1}DeleteButton'>{0}</a>", column.HeaderName, GridName, editUrlParam);
                            }
                            else
                            {
                                if (id == delId)
                                {
                                    if (configuration.DeleteAction == "")
                                    {
                                        configuration.DeleteAction = absUri;
                                    }
                                    string cancelDelUrl = absUri.RemoveUrlParameter("deleteid", GridName).RemoveUrlParameter("edit", GridName);
                                    string delForm = String.Format(@"<form name='{0}DeleteForm' id='{0}DeleteForm' style='display:none;' action='{1}' method='post'>
                                                            <input type='text' name='deleteid' id='deleteid' value='{2}' />
                                                        </form>", GridName, configuration.DeleteAction, modelItem.Id);

                                    value = String.Format("<a href='javascript:  document.{1}DeleteForm.submit()' class='{1}Delete' id='{1}DeleteButton'>Confirm</a>{2}", column.HeaderName, GridName, delForm)
                                    + String.Format(" <a href='{0}' class='{1}Cancel' id='{1}CancelButton'>Cancel</a>", cancelDelUrl, GridName);
                                }

                            }
                        }
                        if (prefCol.Column == PreDefinedColumnsTypes.Edit)
                        {
                                       
                            string editUrl = absUri.SetUrlParameter("edit", id, GridName).RemoveUrlParameter("deleteid", GridName);
                            if (!IsEditMode(GridName, context))
                                value = String.Format("<a href='{2}' class='{1}Edit' id='{1}EditButton'>{0}</a>", column.HeaderName, GridName, editUrl);
                            else
                            {
                                if (System.Convert.ToString(modelItem.Id) == GetQueryParam(context, GridName, "edit"))
                                {
                                    value = String.Format("<a href='{2}' class='{1}Edit' id='{1}CancelButton'>Cancel</a>", column.HeaderName, GridName, absUri.RemoveUrlParameter("edit", GridName));
                                    value = value + String.Format(" <a href='javascript: document.{1}UpdateForm.submit(); void(0); ' class='{1}Edit' id='{1}UpdateButton'>Update</a>", column.HeaderName, GridName);
                                }
                            }
                        }
                        if (prefCol.Column == PreDefinedColumnsTypes.Select)
                        {
                            value = column.GetHtml(modelItem);
                            if (value == null)
                            {
                                //check if some row is selected
                                if (System.Convert.ToString(modelItem.Id) == GetQueryParam(context, GridName, "selectedrow"))
                                    sb.Replace("bgColor" + modelItem.Id, String.Format("style='background-color:{0};'", configuration.SelectedRowBgColor));
                                else
                                    sb.Replace("bgColor" + modelItem.Id, "");

                                string editUrl = absUri.SetUrlParameter("selectedrow", id, GridName).RemoveUrlParameter("deleteid", GridName).RemoveUrlParameter("edit", GridName);
                                value = String.Format("<a href='{2}' class='{1}Select' id='{1}SelectButton'>{0}</a>", "Zaznacz", GridName, editUrl);
                            }
                            
                        }
                    }
                    else
                    {
                        if (context.HttpContext.Request.QueryString[GridName + "edit"] == null ||  column.Editable==false)
                        {
                            value = column.GetHtml(Impromptu.InvokeGet(modelItem, column.DataField));
                        }
                        else
                        {
                            //grid is in edit mode
                            if (System.Convert.ToString(modelItem.Id) ==  GetQueryParam(context, GridName, "edit"))
                            {
                                column.Configuration = configuration;
                 
                                if (column.DataSource != null)
                                {
                                    value = column.Editor(column.DataField, Impromptu.InvokeGet(modelItem, column.DataField), objectName, column.DataSource);
                                }
                                else
                                {
                                    value = column.Editor(column.DataField, Impromptu.InvokeGet(modelItem, column.DataField), objectName);
                                }
                                
                            }
                            else
                            {
                                value = column.GetHtml(Impromptu.InvokeGet(modelItem, column.DataField));
                            }
                        }
                       
                    }
                    if (value != null)
                    {
                        sb.Append(value.ToString());
                    }
                    sb.Append("</td>");
                }

                sb.Append("</tr>");

                
            }

            sb.AppendLine("</tbody>");
            sb.AppendLine("</table>");
            if (IsEditMode(GridName, context))
            {
                sb.AppendLine("</form>");
            }
            
         //   if(deleteTemplate==null)
           //     sb.AppendLine(DeleteTemplate.DeletePopUpBootstrapTemplate(GridName, configuration.DeleteAction, "", ""));

            if (configuration.RegisterJQueryDatePicker)
            {
                var datePicker = "<script type='text/javascript'> $('*[datepickergr=\"dt\"]').datepicker({" + configuration.JQueryDatePickerOptions + "}) </script>";
                sb.AppendLine(datePicker);
            }
 

            sb.AppendLine("<div class='pagination pagination-small pagination-centered'>");
            sb.AppendLine("<ul>");
            double itr = (double)(itemsNumber / (double)configuration.PageSize);
            int pageSizeLength = (int)Math.Ceiling(itr);

            if (pageSizeLength > 1)
            {
                
                for (var i = 0; i < pageSizeLength; i++)
                {
                    string liClass = "";
                    string pagerUrl = "";
                    
                    if (i  == skip-1)
                        liClass = "class='active'";

                    pagerUrl = absUri.SetUrlParameter("page", (i + 1).ToString(), GridName);
                    pagerUrl = pagerUrl.SetUrlParameter("sort", context.HttpContext.Request.QueryString["sort"], GridName);
                    pagerUrl = pagerUrl.SetUrlParameter("sorttype", GetQueryParam(context, GridName, "sorttype"), GridName);

                    sb.AppendLine(String.Format("<li "+liClass+"><a href='{0}'>{1}</a></li>", pagerUrl, i + 1));
                }
            }

            sb.AppendLine("</ul>");
            sb.AppendLine("</div>");


            // Render tag
            MvcHtmlString html = new MvcHtmlString(sb.ToString());

            return html;

        }

        private static string GetParamIfNotEmpty(string GridName, string p, string pageParam)
        {
            if (String.IsNullOrEmpty(pageParam))
            {
                return "";
            }
            else
            {
                return ("?" + GridName+p + "=" + pageParam);
            }
        }

        private static bool IsEditMode(string GridName, RequestContext context)
        {
            return context.HttpContext.Request.QueryString[GridName + "edit"] != null;
        }

  

        private static string GetQueryParam(RequestContext context, string gridname, string name)
        {
            return context.HttpContext.Request.QueryString[gridname+name];
        }

        private static string GetCurrentUrl(RequestContext context)
        {
            return "http://" + context.HttpContext.Request.Url.Authority + context.HttpContext.Request.Url.AbsolutePath;
            
        }

        public static string ParamGenerator(string name, string param)
        {
            return name + param;
        }
    }



    public static class UrlExtensions
    {

        public static string SetUrlParameter(this string url, string paramName, string value, string prefix)
        {
            if(String.IsNullOrEmpty(value))
                return url;

            return new Uri(url).SetParameter(paramName, value, prefix).ToString();
        }

        public static string RemoveUrlParameter(this string url, string paramName, string prefix)
        {
            return new Uri(url).DeleteParameter(paramName, prefix).ToString();
        }

        public static Uri SetParameter(this Uri url, string paramName, string value, string prefix)
        {
            var queryParts = HttpUtility.ParseQueryString(url.Query);
            queryParts[prefix+paramName] = value;
            
            return new Uri(url.AbsoluteUriExcludingQuery() + '?' + queryParts.ToString());
        }

        public static string AbsoluteUriExcludingQuery(this Uri url)
        {
            return url.AbsoluteUri.Split('?').FirstOrDefault() ?? String.Empty;
        }

        public static Uri DeleteParameter(this Uri url, string paramName, string prefix)
        {
            var queryParts = HttpUtility.ParseQueryString(url.Query);
            queryParts.Remove(prefix + paramName);

            return new Uri(url.AbsoluteUriExcludingQuery() + '?' + queryParts.ToString());
        }
    }

}
